package devices

import (
	"context"
	"devops-super/api"
	v1 "devops-super/api/product/v1"
	"devops-super/internal/consts"
	"devops-super/internal/consts/cacheConsts"
	"devops-super/internal/dao"
	"devops-super/internal/model/do"
	"devops-super/internal/model/entity"
	"devops-super/internal/model/mymodel"
	"devops-super/internal/service"
	"errors"
	"github.com/gogf/gf/v2/errors/gcode"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/os/glog"
	"github.com/gogf/gf/v2/os/gtime"
	"time"
)

// sDevice 设备逻辑实现
type sDevice struct{}

func init() {
	service.RegisterDevice(New())
}

// New 创建设备逻辑实例
func New() *sDevice {
	return &sDevice{}
}

// Add 新增设备
func (*sDevice) Add(ctx context.Context, req *v1.DeviceAddReq) (res *v1.DeviceAddRes, err error) {
	isSuperDept, currentUser, _ := service.User().IsCurrentUserSuperDept(ctx)
	if !isSuperDept && currentUser.DeptId != int(req.DeptId) {
		return nil, errors.New("permission denied")
	}

	in := &entity.AppDevice{
		DeviceNo:    req.DeviceNo,
		Name:        req.Name,
		ChannelNo:   req.ChannelNo,
		ProductId:   req.ProductId,
		DeptId:      req.DeptId,
		Type:        req.Type,
		GroupId:     int64(req.GroupId),
		ParentId:    req.ParentId,
		Remark:      req.Remark,
		Lng:         req.Lng,
		Lat:         req.Lat,
		Address:     req.Address,
		Image:       req.Image,
		OpenStatus:  req.OpenStatus,
		CreatedAt:   gtime.Now(),
		NetworkType: req.NetworkType,
	}

	// 检查设备编号是否已存在
	count, _ := dao.AppDevice.Ctx(ctx).Where(dao.AppDevice.Columns().DeviceNo, in.DeviceNo).Count()
	if count > 0 {
		return nil, errors.New("设备编号已存在")
	}

	// 对于子设备，检查父设备是否存在
	if in.Type == 2 && in.ParentId > 0 {
		parentCount, _ := dao.AppDevice.Ctx(ctx).WherePri(in.ParentId).Count()
		if parentCount == 0 {
			return nil, errors.New("父设备不存在")
		}
	}
	rowMqtt := &entity.AppDeptToServerRecord{}
	rowOss := &entity.AppDeptToServerRecord{}
	rowDatabase := &entity.AppDeptToServerRecord{}
	col1 := dao.AppDeptToServerRecord.Columns()
	err = dao.AppDeptToServerRecord.Ctx(ctx).Where(col1.DeptId, in.DeptId).Where(col1.Type, consts.ClusterTypeMqtt).Scan(&rowMqtt)
	if err != nil {
		err = errors.New("allocate mqtt-info for your dept")
		return nil, err
	}
	err = dao.AppDeptToServerRecord.Ctx(ctx).Where(col1.DeptId, in.DeptId).Where(col1.Type, consts.ClusterTypeDatabase).Scan(&rowDatabase)
	if err != nil {
		err = errors.New("allocate database-info for your dept")
		return nil, err
	}
	err = dao.AppDeptToServerRecord.Ctx(ctx).Where(col1.DeptId, in.DeptId).Where(col1.Type, consts.ClusterTypeOss).Scan(&rowOss)
	if err != nil {
		err = errors.New("allocate oss-info for your dept")
		return nil, err
	}
	if rowMqtt.ServerKey == "" || rowDatabase.ServerKey == "" || rowOss.ServerKey == "" {
		err = errors.New("allocate server for your dept first")
		return
	}
	in.MqttServer = rowMqtt.ServerKey
	in.DatabaseServer = rowDatabase.ServerKey
	in.OssServer = rowOss.ServerKey
	// 插入设备记录
	insertId, err := dao.AppDevice.Ctx(ctx).InsertAndGetId(in)
	// 插入设备记录后设置缓存
	if err == nil {
		cacheKey := cacheConsts.GetDeviceInfoKey(in.DeviceNo)

		// 避免字段不全 重新查一遍
		err := dao.AppDevice.Ctx(ctx).WherePri(insertId).Scan(&in)
		if err != nil {
			return nil, err
		}
		_ = service.GetCacheInstance().Set(ctx, cacheKey, in, 0)
	}

	return
}

// Upt 更新设备
func (*sDevice) Upt(ctx context.Context, req *v1.DeviceUptReq) (res *v1.DeviceUptRes, err error) {
	isSuperDept, currentUser, _ := service.User().IsCurrentUserSuperDept(ctx)
	if !isSuperDept && currentUser.DeptId != int(req.DeptId) {
		return nil, errors.New("permission denied")
	}
	in := &entity.AppDevice{
		Id:          uint64(req.Id),
		DeviceNo:    req.DeviceNo,
		Name:        req.Name,
		ChannelNo:   req.ChannelNo,
		ProductId:   req.ProductId,
		DeptId:      req.DeptId,
		Type:        req.Type,
		GroupId:     int64(req.GroupId),
		ParentId:    req.ParentId,
		Remark:      req.Remark,
		Lng:         req.Lng,
		Lat:         req.Lat,
		Address:     req.Address,
		Image:       req.Image,
		OpenStatus:  req.OpenStatus,
		CreatedAt:   gtime.Now(),
		NetworkType: req.NetworkType,
	}
	// 检查设备编号是否已被其他设备使用
	count, _ := dao.AppDevice.Ctx(ctx).Where(dao.AppDevice.Columns().DeviceNo, in.DeviceNo).
		WhereNot(dao.AppDevice.Columns().Id, req.Id).Count()
	if count > 0 {
		return nil, errors.New("设备编号已存在")
	}

	rowMqtt := &entity.AppDeptToServerRecord{}
	rowOss := &entity.AppDeptToServerRecord{}
	rowDatabase := &entity.AppDeptToServerRecord{}
	col1 := dao.AppDeptToServerRecord.Columns()
	err = dao.AppDeptToServerRecord.Ctx(ctx).Where(col1.DeptId, in.DeptId).Where(col1.Type, consts.ClusterTypeMqtt).Scan(&rowMqtt)
	if err != nil {
		err = errors.New("allocate mqtt-info for your dept")
		return nil, err
	}
	err = dao.AppDeptToServerRecord.Ctx(ctx).Where(col1.DeptId, in.DeptId).Where(col1.Type, consts.ClusterTypeDatabase).Scan(&rowDatabase)
	if err != nil {
		err = errors.New("allocate database-info for your dept")
		return nil, err
	}
	err = dao.AppDeptToServerRecord.Ctx(ctx).Where(col1.DeptId, in.DeptId).Where(col1.Type, consts.ClusterTypeOss).Scan(&rowOss)
	if err != nil {
		err = errors.New("allocate oss-info for your dept")
		return nil, err
	}
	if rowMqtt.ServerKey == "" || rowDatabase.ServerKey == "" || rowOss.ServerKey == "" {
		err = errors.New("allocate server for your dept first")
		return nil, err
	}
	in.MqttServer = rowMqtt.ServerKey
	in.DatabaseServer = rowDatabase.ServerKey
	in.OssServer = rowOss.ServerKey

	// 更新设备记录
	_, err = dao.AppDevice.Ctx(ctx).WherePri(in.Id).OmitNilData().Data(in).Update()
	// 更新设备记录后更新缓存
	if err == nil {
		cacheKey := cacheConsts.GetDeviceInfoKey(in.DeviceNo)
		// 避免字段不全 重新查一遍
		err := dao.AppDevice.Ctx(ctx).WherePri(in.Id).Scan(&in)
		if err != nil {
			return nil, err
		}
		_ = service.GetCacheInstance().Set(ctx, cacheKey, in, 0)
	}
	return
}

// GetPageLst 分页获取设备列表
func (*sDevice) GetPageLst(ctx context.Context, req *v1.DeviceGetPageLstReq) (out *api.PageLstRes[*mymodel.AppDeviceResponse], err error) {
	out = &api.PageLstRes[*mymodel.AppDeviceResponse]{}
	m := dao.AppDevice.Ctx(ctx).Safe(true)

	// 产品ID筛选
	if req.ProductId > 0 {
		m = m.Where(dao.AppDevice.Columns().ProductId, req.ProductId)
	}

	// 设备编号筛选
	if req.DeviceNo != "" {
		m = m.Where(dao.AppDevice.Columns().DeviceNo+" like ? ", "%"+req.DeviceNo+"%")
	}

	// 设备名称筛选
	if req.Name != "" {
		m = m.Where(dao.AppDevice.Columns().Name+" like ? ", "%"+req.Name+"%")
	}

	// 激活状态筛选
	if req.OpenStatus != "" {
		m = m.Where(dao.AppDevice.Columns().OpenStatus, req.OpenStatus)
	}

	// 状态筛选
	if req.Status != "" {
		m = m.Where(dao.AppDevice.Columns().Status, req.Status)
	}

	// 部门ID筛选
	if req.DeptId > 0 {
		m = m.Where(dao.AppDevice.Columns().DeptId, req.DeptId)
	}

	// 设备分组ID筛选
	if req.GroupId > 0 {
		m = m.Where(dao.AppDevice.Columns().GroupId, req.GroupId)
	}

	// 按时间范围筛选
	if req.TimeRange != nil {
		if len(req.TimeRange) != 2 {
			return nil, gerror.NewCode(gcode.CodeInvalidParameter, "时间范围参数格式错误")
		}
		// 新增时间转换逻辑
		startTime, err := time.Parse(time.RFC3339, req.TimeRange[0])
		if err != nil {
			return nil, gerror.WrapCode(gcode.CodeInvalidParameter, err, "开始时间格式错误")
		}
		endTime, err := time.Parse(time.RFC3339, req.TimeRange[1])
		if err != nil {
			return nil, gerror.WrapCode(gcode.CodeInvalidParameter, err, "结束时间格式错误")
		}
		timeLayout := "2006-01-02 15:04:05"
		timeStr0 := startTime.Local().Format(timeLayout)
		timeStr1 := endTime.Local().Format(timeLayout)
		m = m.WhereBetween(dao.AppDevice.Columns().CreatedAt, timeStr0, timeStr1)
	}

	// 分页查询获取实体数据
	var entities []*entity.AppDevice
	count, err := m.Count()
	if err != nil {
		return nil, err
	}
	out.Total = count

	if count > 0 {
		if err = m.Offset(req.Offset()).Limit(req.Limit()).Scan(&entities); err != nil {
			return nil, err
		}

		// 转换为响应模型
		out.List = make([]*mymodel.AppDeviceResponse, len(entities))

		// 收集所有相关ID
		var deptIds, groupIds, productIds []int64
		for _, e := range entities {
			if e.DeptId > 0 {
				deptIds = append(deptIds, int64(e.DeptId))
			}
			if e.GroupId > 0 {
				groupIds = append(groupIds, e.GroupId)
			}
			if e.ProductId > 0 {
				productIds = append(productIds, e.ProductId)
			}
		}

		// 批量查询部门信息
		deptMap := make(map[int64]string)
		if len(deptIds) > 0 {
			var depts []*entity.Dept
			if err := dao.Dept.Ctx(ctx).Where("id IN (?)", deptIds).Scan(&depts); err != nil {
				return nil, err
			}
			for _, dept := range depts {
				deptMap[int64(dept.Id)] = dept.Name
			}
		}

		// 批量查询设备分组信息
		groupMap := make(map[int64]string)
		if len(groupIds) > 0 {
			var groups []*entity.AppDeviceGroup
			if err := dao.AppDeviceGroup.Ctx(ctx).Where("id IN (?)", groupIds).Scan(&groups); err != nil {
				return nil, err
			}
			for _, group := range groups {
				groupMap[group.Id] = group.Name
			}
		}

		// 批量查询产品信息
		productMap := make(map[int64]string)
		if len(productIds) > 0 {
			var products []*entity.AppProduct
			if err := dao.AppProduct.Ctx(ctx).Where("id IN (?)", productIds).Scan(&products); err != nil {
				return nil, err
			}
			for _, product := range products {
				productMap[int64(product.Id)] = product.Name
			}
		}

		// 赋值响应数据并拼接名称
		for i, e := range entities {
			out.List[i] = &mymodel.AppDeviceResponse{
				Id:             e.Id,
				DeviceNo:       e.DeviceNo,
				Name:           e.Name,
				ChannelNo:      e.ChannelNo,
				ProductId:      e.ProductId,
				ProductName:    productMap[e.ProductId], // 拼接产品名称
				DeptId:         e.DeptId,
				DeptName:       deptMap[int64(e.DeptId)], // 拼接部门名称
				GroupId:        e.GroupId,
				GroupName:      groupMap[e.GroupId], // 拼接设备分组名称
				Type:           e.Type,
				Remark:         e.Remark,
				Lng:            e.Lng,
				Lat:            e.Lat,
				Address:        e.Address,
				Image:          e.Image,
				Status:         e.Status,
				StatusTime:     e.StatusTime,
				CreatedAt:      e.CreatedAt,
				UpdatedAt:      e.UpdatedAt,
				OpenStatus:     e.OpenStatus,
				OpenStatusTime: e.OpenStatusTime,
				MqttServer:     e.MqttServer,
				DatabaseServer: e.DatabaseServer,
				OssServer:      e.OssServer,
				ParentId:       e.ParentId,
				Version:        e.Version,
			}
		}
	}

	return
}

// UptStatus 更新设备状态   根据id去改
func (s *sDevice) UptStatus(ctx context.Context, id int, status int) (err error) {
	deviceInfo, err := service.Device().GetOne(ctx, id)
	if err != nil {
		return err
	}
	if deviceInfo == nil || deviceInfo.DeviceNo == "" {
		return errors.New("device not found")
	}
	err = s.ChangeDeviceOnlineStatus(ctx, deviceInfo.DeviceNo, status)
	if err != nil {
		return err
	}
	return
}

// Del 删除设备
func (s *sDevice) Del(ctx context.Context, id int) (err error) {
	// 先获取设备信息以便删除缓存
	var device *entity.AppDevice
	device, err = s.GetOne(ctx, id)
	if err != nil {
		return err
	}
	isSuperDept, currentUser, _ := service.User().IsCurrentUserSuperDept(ctx)
	if !isSuperDept && currentUser.DeptId != int(device.DeptId) {
		return errors.New("permission denied")
	}

	// 删除物联网数据
	err = service.Iotdata().DelByDeviceNo(ctx, device.DeviceNo)
	if err != nil {
		return err
	}

	// 删除设备
	_, err = dao.AppDevice.Ctx(ctx).WherePri(id).Delete()
	// 删除缓存
	if err == nil {
		cacheKey := cacheConsts.GetDeviceInfoKey(device.DeviceNo)
		_, _ = service.GetCacheInstance().Remove(ctx, cacheKey)
	}
	return
}

// GetOne 获取单条设备
func (*sDevice) GetOne(ctx context.Context, id int) (out *entity.AppDevice, err error) {
	out = &entity.AppDevice{}
	err = dao.AppDevice.Ctx(ctx).WherePri(id).Scan(&out)
	return
}

// GetOptions 获取设备选项
func (*sDevice) GetOptions(ctx context.Context, in *v1.DeviceGetOptionsReq) (out *v1.DeviceGetOptionsRes, err error) {
	var data []*entity.AppDevice
	m := dao.AppDevice.Ctx(ctx)
	if in.DeptId > 0 {
		m = m.Where(dao.AppDevice.Columns().DeptId, in.DeptId)
	}
	if in.ProductId > 0 {
		m = m.Where(dao.AppDevice.Columns().ProductId, in.ProductId)
	}
	if in.DeviceGroupId > 0 {
		m = m.Where(dao.AppDevice.Columns().GroupId, in.DeviceGroupId)
	}
	if in.Type != "" {
		m = m.Where(dao.AppDevice.Columns().Type, in.Type)
	}
	if err = m.Scan(&data); err != nil {
		return
	}

	out = new(v1.DeviceGetOptionsRes)
	out.Options = make([]*api.FormOptionsItem, 0, len(data))

	// 默认value字段和label字段
	valueField := "deviceNo"
	labelField := "name"

	// 自定义字段
	if in.FieldKey != "" {
		valueField = in.FieldKey
	}
	if in.FieldLabel != "" {
		labelField = in.FieldLabel
	}

	// 构建选项列表
	for _, v := range data {
		item := &api.FormOptionsItem{}
		// 根据反射获取自定义字段值
		if valueField == "id" {
			item.Value = v.Id
		} else if valueField == "deviceNo" {
			item.Value = v.DeviceNo
		} else {
			item.Value = v.Id
		}

		if labelField == "name" {
			item.Label = v.Name
		} else if labelField == "deviceNo" {
			item.Label = v.DeviceNo
		} else {
			item.Label = v.Name
		}

		out.Options = append(out.Options, item)
	}

	return
}

// GetByDeviceNo 根据设备编号获取设备信息
func (*sDevice) GetByDeviceNo(ctx context.Context, deviceNo string) (out *entity.AppDevice, err error) {
	cacheKey := cacheConsts.GetDeviceInfoKey(deviceNo)

	// 先从缓存获取
	if v, _ := service.GetCacheInstance().Get(ctx, cacheKey); !v.IsEmpty() {
		out = &entity.AppDevice{}
		if err = v.Struct(out); err == nil {
			return out, nil
		}
	}

	// 缓存未命中则查询数据库
	err = dao.AppDevice.Ctx(ctx).Where(dao.AppDevice.Columns().DeviceNo, deviceNo).Scan(&out)
	if err != nil {
		glog.Line().Info(ctx, err)
		return nil, err
	}

	// 设置缓存
	_ = service.GetCacheInstance().Set(ctx, cacheKey, out, 0)
	return out, nil
}

// ChangeDeviceOnlineStatus 根据设备编号获取设备信息
func (s *sDevice) ChangeDeviceOnlineStatus(ctx context.Context, deviceNo string, status int) (err error) {
	deviceInfo, err := s.GetByDeviceNo(ctx, deviceNo)
	if err != nil || deviceInfo == nil || deviceInfo.Id == 0 {
		return errors.New("device not exist")
	}
	if deviceInfo.Status != status {
		deviceInfo.Status = status
		// 更新设备记录
		_, err = dao.AppDevice.Ctx(ctx).WherePri(deviceInfo.Id).OmitNilData().Data(deviceInfo).Update()
		// 更新设备记录后更新缓存
		if err != nil {
			return err
		}
		cacheKey := cacheConsts.GetDeviceInfoKey(deviceInfo.DeviceNo)
		_ = service.GetCacheInstance().Set(ctx, cacheKey, deviceInfo, 0)
		// 有子设备也要变更状态
		if deviceInfo.Type == 1 {
			var subDevices []*entity.AppDevice
			err = dao.AppDevice.Ctx(ctx).Where(dao.AppDevice.Columns().ParentId, deviceInfo.Id).Scan(&subDevices)
			if err != nil {
				return err
			}
			if subDevices != nil && len(subDevices) > 0 {
				for _, subDevice := range subDevices {
					return s.ChangeDeviceOnlineStatus(ctx, subDevice.DeviceNo, status)
				}
			}
		}
	}
	return
}

func (s *sDevice) ChangeVersion(ctx context.Context, deviceNo string, version string) (err error) {
	if version == "" {
		return
	}
	deviceInfo, err := s.GetByDeviceNo(ctx, deviceNo)
	if err != nil || deviceInfo == nil || deviceInfo.Id == 0 {
		return errors.New("device not exist")
	}
	if deviceInfo.Status == consts.OpenStatusInactive {
		return
	}
	if deviceInfo.Version != version {
		deviceInfo.Version = version
		_, err := dao.AppDevice.Ctx(ctx).WherePri(deviceInfo.Id).Update(do.AppDevice{
			Version: version,
		})
		if err != nil {
			return err
		}
		cacheKey := cacheConsts.GetDeviceInfoKey(deviceInfo.DeviceNo)
		_ = service.GetCacheInstance().Set(ctx, cacheKey, deviceInfo, 0)
	}
	return
}

func (s *sDevice) GetCopies(ctx context.Context, req *v1.DeviceGetCopiesReq) (res *v1.DeviceGetCopiesRes, err error) {
	res = new(v1.DeviceGetCopiesRes)
	deviceInfo, err := s.GetByDeviceNo(ctx, req.DeviceNo)
	if err != nil || deviceInfo == nil || deviceInfo.Id == 0 {
		return nil, errors.New("device not exist")
	}
	var copies []*entity.AppDevice
	_ = dao.AppDevice.Ctx(ctx).
		Where(dao.AppDevice.Columns().DeptId, deviceInfo.DeptId).
		Where(dao.AppDevice.Columns().ProductId, deviceInfo.ProductId).
		Where(dao.AppDevice.Columns().GroupId, deviceInfo.GroupId).
		WhereNot(dao.AppDevice.Columns().DeviceNo, deviceInfo.DeviceNo).
		Scan(&copies)
	res.List = copies
	return
}
